﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using Xceed.Silverlight.Data.Utils;

namespace Xceed.Silverlight.Data
{
  internal class DataSourceCollection : ICollection, INotifyCollectionChanged, ISLWeakEventListener
  {
    internal DataSourceCollection( IEnumerable dataSource )
    {
      if( dataSource == null )
        throw new ArgumentNullException( "dataSource" );

      m_dataSource = dataSource;

      // Listen to notification events if they are supported.
      INotifyCollectionChanged observableCollection = dataSource as INotifyCollectionChanged;
      if( observableCollection != null )
      {
        SLCollectionChangedEventManager.AddListener( observableCollection, this );
      }

      // Retrieve the type of the items in the collection.
      m_elementType = DataSourceCollection.GetElementType( dataSource.GetType() );
      if( m_elementType == null)
      {
        ICollectionView collectionView = dataSource as ICollectionView;
        IEnumerable sourceCollection = ( collectionView != null ) ? collectionView.SourceCollection : null;

        if( sourceCollection != null )
        {
          m_elementType = DataSourceCollection.GetElementType( sourceCollection.GetType() );
        }
      }

      if( m_elementType == null )
      {
        m_elementType = typeof( object );
      }
    }


    #region Count Property

    public int Count
    {
      get
      {
        lock( ( ( ICollection )this ).SyncRoot )
        {
          ICollection sourceCollection = this.GetSourceCollection();
          int count = 0;

          if( sourceCollection != null )
          {
            count = sourceCollection.Count;
          }
          else
          {
            foreach( object item in this.GetSourceEnumerable() )
            {
              count++;
            }
          }

          return count;
        }
      }
    }

    #endregion

    #region ElementType Property

    internal Type ElementType
    {
      get
      {
        return m_elementType;
      }
    }

    private Type m_elementType;

    #endregion


    #region CollectionChanged Event

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion


    public IEnumerator GetEnumerator()
    {
      return this.GetSourceEnumerable().GetEnumerator();
    }

    private ICollection GetSourceCollection()
    {
      ICollection sourceCollection = m_dataSource as ICollection;

      // Try to get a ICollection from a ICollectionView.
      if( sourceCollection == null )
      {
        ICollectionView collectionView = m_dataSource as ICollectionView;

        if( collectionView != null )
        {
          sourceCollection = collectionView.SourceCollection as ICollection;
        }
      }

      return sourceCollection;
    }

    private IEnumerable GetSourceEnumerable()
    {
      IEnumerable sourceEnumerable = m_dataSource;
      ICollectionView collectionView = sourceEnumerable as ICollectionView;

      if( collectionView != null )
      {
        sourceEnumerable = collectionView.SourceCollection;
      }

      return sourceEnumerable;
    }

    private static Type GetElementType( Type dataSourceType )
    {
      if( dataSourceType != null )
      {
        if( dataSourceType.IsArray )
          return dataSourceType.GetElementType();

        // Find out the element type through the IEnumerable<> interface.
        foreach( Type interfaceType in dataSourceType.GetInterfaces() )
        {
          if( ( interfaceType.IsGenericType ) && ( interfaceType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) ) )
          {
            return interfaceType.GetGenericArguments()[ 0 ];
          }
        }
      }

      return null;
    }


    #region ICollection Members

    bool ICollection.IsSynchronized
    {
      get
      {
        return true;
      }
    }

    object ICollection.SyncRoot
    {
      get
      {
        return m_syncRoot;
      }
    }

    void ICollection.CopyTo( Array array, int index )
    {
      if( array == null )
        throw new ArgumentNullException( "array" );

      ICollection self = this;
      lock( self.SyncRoot )
      {
        foreach( object item in self )
        {
          array.SetValue( item, index );
          index++;
        }
      }
    }

    #endregion

    #region IWeakEventListener Members

    bool ISLWeakEventListener.ReceiveWeakEvent( Type managerType, object sender, EventArgs e )
    {
      if( managerType == typeof( SLCollectionChangedEventManager ) )
      {
        if( this.CollectionChanged != null )
        {
          this.CollectionChanged( this, ( NotifyCollectionChangedEventArgs )e );
        }

        return true;
      }

      return false;
    }

    #endregion


    #region Private Fields

    private IEnumerable m_dataSource;
    private object m_syncRoot = new object();

    #endregion
  }
}
